Skip to content

feat(mcp): require a USD cap on every real purchase - #69

Merged
cristim merged 1 commit into
mainfrom
feat/51-usd-cap-wiring
Oct 17, 2026
Merged

cristim merged 1 commit into
mainfrom
feat/51-usd-cap-wiring

Conversation

@cristim

@cristim cristim commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

Summary

This is part 2 of 3 for #51. It is stacked on #66: the base is feat/51-usd-spend-cap, so merge #66 first and then retarget this PR to main.

The existing per-call caps bound request size, not money: CUDLY_MCP_MAX_COUNT=50 still admitted 50 x u-24tb1, roughly $286M over three years. Real purchases now also require CUDLY_MCP_MAX_COMMITMENT_USD.

  • Before credentials: authorizeRealPurchase reads the USD cap. Unset, empty, non-numeric, NaN, Inf or <= 0 refuses the purchase before ResolveClient, with no credential or network use. The parsed limit is passed down, so the env var is read once.
  • After ResolveClient, before the idempotency token or PurchaseCommitment: enforceUSDCap (from feat(tools): compute the total USD a purchase commits #66) prices the purchase and refuses a total above the cap. A purchase it cannot price is refused too.
  • GCP: real GCP CUD purchases are now refused, because the pinned library cannot price them (go#321). The owner chose this over bumping the pin first. Re-enabling is tracked in feat(gcp): bump cloud-commitments-go past go#321 and re-enable priced GCP CUD purchases #67.
  • ExecutePurchase's response tail moved into purchaseResponse, which keeps the function under the gocyclo limit. This is a pure extraction.
  • Operator surface:
    • mcpb/manifest.json: env var in all 3 platform blocks, plus a max_commitment_usd user setting whose title matches the refusal message.
    • server.json
    • README spend-cap paragraph and CHANGELOG Breaking / Added / Security entries.

Refs #51 (the issue stays open for part 3, the cumulative budget)

Test changes that narrow coverage (deliberate)

Under the GCP refusal, four tests that asserted a successful real GCP purchase now assert the refusal:

The over-cap refusals are still asserted. The library-boundary GCP cost proof stays in tools/gcp_computeengine_cud_safeguards_test.go. #67 lists all four tests to restore. No test-only bypass seam was added.

rdsOffering in tools/adopted_go_safeguards_test.go gains a price and currency, because the real RDS library path now has to clear the cap.

Verification (local, macOS, at d2e389b)

Fake-cloud and unit evidence only. No real purchase or cloud call is made.

  • gofmt, go vet ./... and make build pass.
  • go test -race -count=1 ./... passes in all 3 packages. golangci-lint and gosec, both run with GOTOOLCHAIN=go1.26.9, report 0 issues.
  • gocyclo -over 10 lists no non-test code. markdownlint passes on README and CHANGELOG, and both JSON files parse.
  • Regression: TestUSDCapRegressionExpensiveRI (EC2 u-24tb1 x 50, $5.72M each, cap $1000). Without the enforceUSDCap call the purchase executes (purchaseCalls == 1, the test fails). With it the call is refused, naming CUDLY_MCP_MAX_COMMITMENT_USD, with purchaseCalls == 0 after the client resolved.
  • TestUSDCapUnsetRefusesBeforeResolve checks that an unset cap leaves the client unresolved and makes no offering call.
  • Mutation probes by the reviewer (scratch copy, restored): each of these fails tests:
    • deleting the cap call
    • skipping the pre-resolve read
    • moving the cap check after PurchaseCommitment

Review

An independent reviewer (Sonnet, high effort, non-author) did two passes. Pass 1 found no high or medium issues. It traced all 8 ExecutePurchase call sites to the single choke point, confirmed previews stay offline, and confirmed the purchaseResponse extraction preserves behaviour. Its four low findings were handled as follows:

  1. CHANGELOG wording on the "required" setting: fixed.
  2. The GCP refusal runs after ResolveClient: accepted. It needs no network, and the docs only promise a pre-credential refusal for an unset cap.
  3. An unproven "same offering is bought" sentence: softened.
  4. Cap refusals are not audited, the same as every existing pre-purchase refusal: follow-up feat(audit): record pre-purchase refusals in the purchase audit log #68.

Pass 2 found no actionable findings. CodeRabbit is not pinged because the org's CR quota is exhausted.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Real purchases now require a positive USD commitment limit, applied per call to the provider’s full-term price. Purchases are refused if pricing is unavailable, invalid, or exceeds the limit.
    • The limit is configurable through CUDLY_MCP_MAX_COMMITMENT_USD; existing quantity limits remain separate.
  • Bug Fixes
    • Real GCP CUD purchases are refused until USD pricing is available.
    • Errors that could expose readable API keys are withheld, and ARCHERA_API_KEY is validated for printable ASCII.
  • Documentation
    • Updated setup and safety guidance to explain pricing checks, per-call limits, and purchase restrictions.

@cristim cristim added urgency/this-sprint Within the current sprint priority/p1 Next up; this sprint severity/high Significant harm impact/all-users Affects every user effort/m Days type/feat New capability triaged Item has been triaged labels Oct 16, 2026
@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: LeanerCloud/cloud-commitments-mcp/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 42d317e0-97d5-4ffe-aeda-f2fd6bf7b45f
📥 Commits

Reviewing files that changed from the base of the PR and between 8569f85 and 928c95c.

📒 Files selected for processing: 15
  • CHANGELOG.md
  • README.md
  • cmd/cudly-mcp/gcp_purchase_protocol_test.go
  • cmd/cudly-mcp/main_test.go
  • mcpb/manifest.json
  • server.json
  • tools/adopted_go_safeguards_test.go
  • tools/commitment_usd.go
  • tools/gcp_computeengine_cud_test.go
  • tools/purchase.go
  • tools/purchase_test.go
  • tools/search_dedupe_test.go
  • tools/spend_caps.go
  • tools/spend_caps_test.go
  • tools/usd_cap_wiring_test.go

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

📝 Walkthrough

Real purchases now require a positive USD commitment cap. The purchase flow prices the full-term commitment using provider offering data and refuses purchases that exceed the cap or cannot be priced. Real GCP CUD purchases remain refused until the pricing dependency can provide a USD total.

Changes: USD commitment cap

Layer / File(s) Summary
USD cap configuration and contract
tools/spend_caps.go, mcpb/manifest.json, server.json, CHANGELOG.md, README.md
The USD cap setting is registered and exposed in server configuration. The documentation describes it as a per-call ceiling for real purchases and states that unpriceable purchases are refused.
Real-purchase cap enforcement
tools/purchase.go, tools/commitment_usd.go, tools/usd_cap_wiring_test.go, tools/purchase_test.go, tools/adopted_go_safeguards_test.go, tools/search_dedupe_test.go
Authorization reads the configured cap. ExecutePurchase checks the commitment total before issuing the purchase. Tests cover cap parsing, pricing errors, and over-cap refusal.
GCP CUD refusal checks
tools/gcp_computeengine_cud_test.go, tools/spend_caps_test.go, cmd/cudly-mcp/gcp_purchase_protocol_test.go, cmd/cudly-mcp/main_test.go
GCP CUD real-purchase tests now expect refusal and verify that no purchase call or insert occurs.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ExecutePurchase
  participant authorizeRealPurchase
  participant committedUSD
  participant GetOfferingDetails
  participant ServiceClient
  ExecutePurchase->>authorizeRealPurchase: read configured USD limit
  authorizeRealPurchase-->>ExecutePurchase: return USD limit
  ExecutePurchase->>committedUSD: calculate recommended commitment
  committedUSD->>GetOfferingDetails: retrieve offering price and currency
  GetOfferingDetails-->>committedUSD: return offering details
  committedUSD-->>ExecutePurchase: return total or pricing error
  ExecutePurchase->>ServiceClient: issue purchase when total is within limit
Loading
Merge Risk: ⚪ Minimal · up to 928c9

Real purchases require a price within the configured cap, while unpriceable GCP purchases are refused as documented. No identified issue currently blocks merging.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 52.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 11 files. (4 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: requiring a USD cap for every real purchase.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 52.17% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 11 files. (4 skipped: 4 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cristim

cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

Merge-driver verdict: CLEAN at d2e389b86a365b5bafcee4b5f9e15f3bfc869543

Scope. This reviews the delta 5ffa001..d2e389b on top of #66. The same independent reviewer reviewed #66 (Sonnet, high effort, cold brief, not the author). It found no blocking findings.

Verified:

  • Every real purchase is capped. All eight purchase tools route through ExecutePurchase. The only PurchaseCommitment call is at tools/purchase.go:664. The order is authorize, then ResolveClient, then enforceUSDCap on the same rec, then PurchaseCommitment. No path bypasses the cap.
  • Previews are not blocked. They return before the cap is read.
  • The cap fails closed. An empty, whitespace, unparsable, NaN, Inf, zero or negative cap refuses the purchase before ResolveClient (TestUSDCapUnsetRefusesBeforeResolve, TestUSDCapEnvParsing).
  • Wording. Refusals use the refusing real purchase: prefix from spend_caps.go, and GCP is "refused" (go#321) with zero provider calls.
  • No weakened tests. The removed lines are GCP success assertions, replaced by refusal assertions. The new usd_cap_wiring_test.go covers the $286M regression case.

Mutation probes (3), all killed:

  1. Deleting the enforceUSDCap call made 6 tests fail.
  2. Replacing the cap read with a huge constant made 2 tests fail.
  3. Loosening zero-rejection from <= 0 to < 0 made the env-parsing test fail.

Local verification on macOS (real runs, fixture and mock pricing, no cloud calls):

  • Toolchain: GOTOOLCHAIN=go1.26.9, the version pinned in go.mod. The local go1.27.2 install is broken.
  • go build ./... passes.
  • go test -count=1 ./... passes: 809 tests.
  • Real-account coverage gap: no live purchase was exercised, by design.

Non-blocking notes (for follow-up):

  • GCP is refused only after ResolveClient, so credentials are resolved first. The README says scope checks run before credentials are touched. An early refusal in authorizeRealPurchase would make that claim true.
  • The manifest has max_commitment_usd min: 1, while the server accepts any value above 0.
  • The GCP real-success structured-cost coverage is gone; restore it with the go#321 pin bump.

Gate status:

@cristim

cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

Follow-ups for the non-blocking notes:

@cristim

cristim commented Oct 16, 2026

Copy link
Copy Markdown
Member Author

Automated review of d2e389b: no blocking findings and CI is green. Ready to merge.
USD cap is read before credentials, priced via the provider offering lookup, and fails closed on unset, invalid, non-USD, zero or unpriceable values. GCP CUDs are refused. Wiring and unit tests cover the main paths. No blocking bugs found.

  • (optional) tools/purchase.go:652: The USD cap passes the pre-purchase price check, but the returned total is discarded. Recording it in the audit record or response would help operators. Cap refusals write no audit entry, which is documented in the comment.
  • (optional) CHANGELOG.md:28: One bullet is a very long line, and the Breaking section now has a blank line splitting its bullets. Tidy the wrapping.

Posted by a read-only review sweep. It stays silent on this commit and re-reviews only after a new push.

@cristim
cristim changed the base branch from feat/51-usd-spend-cap to main October 17, 2026 15:22
Per-call caps bounded request size, not money: CUDLY_MCP_MAX_COUNT=50
still admitted 50 x u-24tb1, roughly $286M over three years. Real
purchases now also need CUDLY_MCP_MAX_COMMITMENT_USD. An unset or
invalid value refuses before credentials are resolved; after
ResolveClient the purchase is priced with the helpers from the
previous commit and refused when its total exceeds the cap, before an
idempotency token is derived or anything is bought.

Real GCP CUD purchases are now refused until the library pin includes
the CUD pricing fix (go#321, follow-up #67). GCP tests that asserted a
successful real purchase now assert the refusal; #67 restores them.
ExecutePurchase's response tail moves into purchaseResponse to stay
under the gocyclo limit. Manifest, server.json, README and CHANGELOG
document the new required setting.

Refs #51

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cristim
cristim force-pushed the feat/51-usd-cap-wiring branch from d2e389b to 928c95c Compare October 17, 2026 15:22
@cristim

cristim commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

Rebase note: #66 merged as 61848db. This PR was retargeted to main and rebased with git rebase --onto origin/main 5ffa001, which dropped #66's commits. The new head is 928c95c904aefa59563e35327a34bc7e86e6afb0.

git range-diff 5ffa001..d2e389b origin/main..928c95c904aefa59563e35327a34bc7e86e6afb0:

1:  d2e389b = 1:  928c95c feat(mcp): require a USD cap on every real purchase

The range-diff is empty, so the CLEAN verdict above carries over to 928c95c904aefa59563e35327a34bc7e86e6afb0. The merge waits for full CI on this SHA.

@cristim

cristim commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

Gate met at 928c95c904aefa59563e35327a34bc7e86e6afb0: the CLEAN verdict carries over through the identical range-diff above. CI is green on this SHA (CI - Build & Test, pre-commit, MCP server.json), mergeable, and labels match #51. CR waived: quota, adversarial review + local verification + green CI. Merging.

@cristim
cristim merged commit a14f10c into main Oct 17, 2026
15 checks passed
@cristim
cristim deleted the feat/51-usd-cap-wiring branch October 17, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/m Days impact/all-users Affects every user priority/p1 Next up; this sprint severity/high Significant harm triaged Item has been triaged type/feat New capability urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant